下面代码输出什么?
1 | @implementation Son : Father |
都输出
Son
。self
的class
和预想的一样,怎么supe
r 的class
也是Son
?
self
是类的隐藏的参数,指向当前调用方法的类。super
并不是隐藏的参数,它只是一个“编译器指示符”,它和 self
指向的是相同的消息接收者,拿上面的代码为例,不论是用 [self class]
还是 [super class]
,接收“class”
这个消息的接收者都是 当前类 这个对象。不同的是,super
告诉编译器,当调用 class
的方法时,要去调用父类的方法,而不是本类里的。
当使用 self
调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super
时,则从父类的方法列表中开始找。然后调用父类的这个方法
这种机制到底底层是如何实现的?其实当调用类方法的时候,编译器会将方法调用转成一个C
函数方法调用,Apple
的 objcRuntimeRef
上说:
1 | Sending Messages |
可以看到会转成调用上面 4 个方法中的一个,由于 _stret
系列的和没有_stret
的那两个类似,先只关注objc_msgSend
和 objc_msgSendSuper
两个方法。当使用 [self class]
调用时,会使用 objc_msgSend
的函数,先看下 objc_msgSend
的函数定义:
1 | id objc_msgSend(id theReceiver, SEL theSelector, ...) |
而当使用 [super class]
调用时,会使用 objc_msgSendSuper
函数,看下 objc_msgSendSuper
的函数定义:
1 | id objc_msgSendSuper(struct objc_super *super, SEL op, ...) |
第一个成员是 receiver
, 类似于上面的 objc_msgSend
函数第一个参数self
第二个成员是记录当前类的父类是什么,告诉程序从父类中开始找方法,找到方法后,最后内部是使用 objc_msgSend(objc_super->receiver, @selector(class))
去调用, 此时已经和[self class]
调用相同了,故上述输出结果仍然返回 Son
里面的调用机制大体就是这样了,以上面的分析,回过头来看开始的代码,当输出[self class]
和 [super class]
时,是个怎样的过程。
当使用
[self class]
时,这时的self
是Son
,在使用objc_msgSend
时,第一个参数是receiver
也就是self
。第二个参数,要先找到class
这个方法的selector
,先从Son
这个类开始找,没有,然后到Son
的父类Father
中去找,也没有,再去Father
的父类NSObject
去找,一层一层向上找之后,在NSObject
的类中发现这个class
方法,而NSObject
的这个class
方法,就是返回receiver
的类别,所以这里输出Son
。
当使用
[super class]
时,这时要转换成objc_msgSendSuper
的方法。先构造objc_super
的结构体吧,第一个成员变量就是self
,第二个成员变量是Father
,然后要找class
这个selector
,先去superClass
也就是Father
中去找,没有,然后去Father
的父类中去找,结果还是在NSObject
中找到了。然后内部使用函数objc_msgSend(objc_super->receiver, @selector(class))
去调用,此时已经和我们用[self class]
调用时相同了,此时的receiver
还是Son*son
,所以这里返回的也是Son
。